package service

import (
	"context"
	"errors"
	"sync"
	"time"
	"wanfang-translation/internal/service/check"
	"wanfang-translation/internal/service/exception"

	"github.com/spf13/viper"
)

/*
	查重控制模块，使用每个接口的QPS需要小于10
	调用过程：
	1、调用AddCheck，添加送检信息（英文库id：e93c68a5-9e98-46b8-8b54-290ee0967757，product填写“bytest”），会返回check_id
	2、根据check_id调用Check，开始检测
	3、根据check_id调用GetCheckStatus，获取检测状态，（当检测状态为SUCCESS时，结果里有copy_percent相似比值）
	4、当检测状态为SUCCESS时，根据check_id调用GetCheckResult获取检测结果（结果是个json字符串，如果需要具体解释，可以再问我要）
*/

// TokenBucket 令牌桶结构体
type TokenBucket struct {
	capacity     int           // 桶的容量
	tokens       int           // 当前令牌数量，每次请求都是从令牌桶里面取令牌
	fillInterval time.Duration // 令牌填充间隔
	mutex        sync.Mutex    // 互斥锁
}

// NewTokenBucket 创建新的令牌桶
func NewTokenBucket(capacity int, fillInterval time.Duration) *TokenBucket {
	tb := &TokenBucket{
		capacity:     capacity,
		tokens:       capacity,
		fillInterval: fillInterval,
	}

	// 定时填充令牌
	go func() {
		ticker := time.NewTicker(fillInterval)
		for range ticker.C {
			tb.mutex.Lock()
			if tb.tokens < tb.capacity {
				tb.tokens++
			}
			tb.mutex.Unlock()
		}
	}()

	return tb
}

// Allow 检查是否允许请求
func (tb *TokenBucket) Allow() bool {
	tb.mutex.Lock()
	defer tb.mutex.Unlock()

	if tb.tokens > 0 {
		tb.tokens--
		return true
	}

	return false
}

// CheckServiceBucket 控制服务结构体
type CheckServiceBucket struct {
	addCheckBucket  *TokenBucket // AddCheck 接口的令牌桶
	checkBucket     *TokenBucket // Check 接口的令牌桶，开始检测
	getStatusBucket *TokenBucket // GetCheckStatus 接口的令牌桶，获取检测状态
	getResultBucket *TokenBucket // GetCheckResult 接口的令牌桶，获取检测结果
	cnIDs           []string
	enIDs           []string
}

// NewCheckService 创建新的控制服务
func NewCheckService() *CheckServiceBucket {
	// 从配置文件中读取数据库 ID
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath("config")

	if err := viper.ReadInConfig(); err != nil {
		panic("无法读取配置文件: " + err.Error())
	}

	if err := viper.ReadInConfig(); err != nil {
		panic("无法读取配置文件: " + err.Error())
	}

	// 从配置文件中读取万方查重数据库 ID
	cnIDs := viper.GetStringSlice("wanfang_dbs.cn_ids")
	enIDs := viper.GetStringSlice("wanfang_dbs.en_ids")

	return &CheckServiceBucket{
		addCheckBucket:  NewTokenBucket(10, time.Second),
		checkBucket:     NewTokenBucket(10, time.Second),
		getStatusBucket: NewTokenBucket(10, time.Second),
		getResultBucket: NewTokenBucket(10, time.Second),
		cnIDs:           cnIDs,
		enIDs:           enIDs,
	}
}

// waitForToken 等待令牌桶允许请求
func (cs *CheckServiceBucket) waitForToken(bucket *TokenBucket) error {
	// 检测QPS是否超过限制，如果超过限制则等待0.5s，超时时间为10s
	for i := 0; i < 20; i++ {
		if bucket.Allow() {
			return nil
		}
		time.Sleep(500 * time.Millisecond)
	}
	return errors.New("请求超时")
}

// 定义语言的枚举类型
const (
	Chinese = "cn"
	English = "en"
)

// AddCheck 控制 AddCheck 接口 inputLanguage 为输入语言(en 或 cn)
func (cs *CheckServiceBucket) AddCheck(inputLanguage string, client check.CheckClient, title string, content string) (string, error) {
	if err := cs.waitForToken(cs.addCheckBucket); err != nil {
		return "", err
	}
	// 根据输入语言选择对应的数据库 ID
	var db_ids []string
	if inputLanguage == Chinese {
		db_ids = cs.cnIDs
	} else if inputLanguage == English {
		db_ids = cs.enIDs
	} else {
		return "", errors.New("不支持的语言类型")
	}

	// 调用实际的 AddCheck 接口逻辑
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()

	addCheck := check.AddCheckRequest{
		Product:    "bytest",
		CheckDbIds: db_ids,
		Title:      title,
		Content:    content,
	}

	resp, err := client.AddCheck(ctx, &addCheck)

	if err != nil {
		exception.LogError(exception.NewProgramError("调用 AddCheck 失败: " + err.Error()))
		return "", err
	}
	// 检查返回接口是否正确
	if resp.GetCheckId() == "" {
		return "", errors.New("AddCheck 接口返回 check_id 为空")
	}

	return resp.GetCheckId(), nil
}

// Check 控制 Check 接口，对指定ID开始检测服务
func (cs *CheckServiceBucket) Check(client check.CheckClient, checkId string) (int, error) {
	if err := cs.waitForToken(cs.checkBucket); err != nil {
		return 0, err
	}

	// 调用实际的 Check 接口逻辑
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()

	checkRequest := check.CheckRequest{
		CheckId: checkId,
	}

	resp, err := client.Check(ctx, &checkRequest)
	if err != nil {
		exception.LogError(exception.NewProgramError("调用 Check 失败: " + err.Error()))
		return 0, err
	}

	return int(resp.GetWaitTime()), nil
}

// GetCheckStatus 控制 GetCheckStatus 接口，可以批量查询
func (cs *CheckServiceBucket) GetCheckStatus(client check.CheckClient, checkIds []string) ([]*check.CheckStatus, error) {
	if err := cs.waitForToken(cs.getStatusBucket); err != nil {
		return nil, err
	}

	// 调用实际的 GetCheckStatus 接口逻辑
	getCheckStatusRequest := check.GetCheckStatusRequest{
		CheckId: checkIds,
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()

	resp, err := client.GetCheckStatus(ctx, &getCheckStatusRequest)
	if err != nil {
		exception.LogError(exception.NewProgramError("调用 GetCheckStatus 失败: " + err.Error()))
		return nil, err
	}

	// 返回判断状态
	return resp.GetCheckStatus(), nil
}

// GetCheckResult 控制 GetCheckResult 接口 需要保证check_id的status 为 SUCCESS才可以调用该函数
func (cs *CheckServiceBucket) GetCheckResult(client check.CheckClient, checkId string) (string, error) {
	if err := cs.waitForToken(cs.getResultBucket); err != nil {
		return "", err
	}

	// 调用实际的 GetCheckResult 接口逻辑
	getCheckResultRequest := check.GetCheckResultRequest{
		CheckId: checkId,
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()

	resp, err := client.GetCheckResult(ctx, &getCheckResultRequest)
	if err != nil {
		exception.LogError(exception.NewProgramError("调用 GetCheckResult 失败: " + err.Error() + ",checkId: " + checkId))
		return "", err
	}

	return resp.GetCheckResult(), nil
}
